package com.ma.chapter8.stampedlock;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.StampedLock;

import static com.ma.util.Sleeper.sleep;

/**
 * @ClassName TestStampedLock
 * @Author: mayongqiang
 * @DATE 2022/3/26 14:33
 * @Description: 引入戳, 进一步优化读性能
 */
@Slf4j(topic = "TestStampedLock")
public class TestStampedLock {
    public static void main(String[] args) {
        DataContainerStamped dataContainer = new DataContainerStamped(1);

        // 读-读操作并不会加读锁
        //new Thread(() -> {
        //    dataContainer.read(1);
        //}, "t1").start();
        //sleep(0.5);
        //new Thread(() -> {
        //    dataContainer.read(0);
        //}, "t2").start();

        // 读-写操作会补加读锁
        new Thread(() -> {
            dataContainer.read(1);
        }, "t3").start();
        sleep(0.5);
        new Thread(() -> {
            dataContainer.write(100);
        }, "t4").start();
    }

}

@Slf4j(topic = "m.DataContainerStamped")
class DataContainerStamped {
    private int data;
    /*
     * StampedLock 不支持条件变量,不支持可重入
     */
    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        long stamp = lock.tryOptimisticRead();
        log.debug("optimistic read locking ... {}", stamp);
        sleep(readTime);
        if (lock.validate(stamp)) {
            log.debug("read finish...{},data:{}", stamp, data);
            return data;
        }
        //锁升级 读锁
        log.debug("updating to read lock ... {}", stamp);
        try {
            stamp = lock.readLock();
            log.debug("read lock {}", stamp);
            sleep(readTime);
            log.debug("read finish ... {},data:{}", stamp, data);
            return data;
        } finally {
            log.debug("read unlock {}", stamp);
            lock.unlockRead(stamp);
        }
    }


    public void write(int newData) {
        long stamp = lock.writeLock();
        log.debug("write lock {}", stamp);
        try {
            sleep(2);
            this.data = newData;
        } finally {
            log.debug("write unlock {}", stamp);
            lock.unlockWrite(stamp);
        }
    }

}
